; (function (window) {
    // 扫雷的每个格子
    class Cell {
        _rowIndex = 0;
        _colIndex = 0;
        // 当前格子是否为雷
        _isMine = false;
        // 是否用户点击
        _isClicked = false;
        _isShow = false;
        _isFlag = false;
        // 周围雷的数量
        _mineTotal = 0;
        // dom 元素
        _element = null;
        // 上下文，即 Minesweeper 实例
        _ctx = null;

        constructor(context, rowIndex, colIndex) {
            this._ctx = context;
            this._rowIndex = rowIndex;
            this._colIndex = colIndex;
            this._element = document.createElement('div');
            this._element.classList.add('minesweeper-cell');
            // 格子被点击
            this._element.onclick = () => {
                this._isClicked = true;
                this.show();
                if (this._ctx) this._ctx.onCellClick(this);
            };
            this._element.oncontextmenu = (e) => {
                e.preventDefault();
                this._isFlag = !this._isFlag;
                this._element.classList.toggle('is-flag');
                if (this._ctx) this._ctx.onSetFlag(this._isFlag);
            }
        }
        get rowIndex() {
            return this._rowIndex;
        }
        get colIndex() {
            return this._colIndex;
        }
        get isMine() {
            return this._isMine;
        }
        get mineTotal() {
            return this._mineTotal;
        }
        get element() {
            return this._element;
        }
        get isShow() {
            return this._isShow;
        }
        setMine() {
            this._isMine = true;
        }
        setMineTotal(total) {
            if (!this._isMine) {
                this._mineTotal = total;
                if (this._isShow) {
                    this._element.innerText = this._mineTotal ? this._mineTotal.toString() : '';
                }
            }
        }
        show(isBomb = false) {
            if (this._isShow) return;
            this._element.onclick = null;
            this._element.oncontextmenu = null;
            this._isShow = true;
            this._element.classList.remove('is-flag');
            this._element.classList.add('checked');
            if (this._isMine) {
                this._element.classList.add('is-mine');
                if (this._isClicked || isBomb) {
                    this._element.classList.add('is-bomb');
                }
            } else {
                this._element.innerText = this._mineTotal ? this._mineTotal.toString() : '';
            }
        }
    }
    // 扫雷主类
    class Minesweeper {
        // 行数
        _rowNum = 10;
        // 列数
        _colNum = 10;
        // 雷数
        _mineNum = 10;
        // 已显示的格子数
        _showTotal = 0;
        _flagTotal = 0;
        // 记录所有网格信息
        _grid = [];
        _els = {
            wrapper: null,
            container: null,
            flag: null,
            mine: null,
        };
        // 是否已初始化，防止第一次爆雷
        _isInitial = false;
        _isOver = false;
        constructor(container, rowNum = 10, colNum = 10, mineNum = 10) {
            let wrapperEl = container ? document.querySelector(container) : document.body;
            wrapperEl = wrapperEl || document.body;
            this._els.wrapper = wrapperEl;
            this._els.container = document.createElement('div');
            this._els.container.classList.add('minesweeper');
            this._els.wrapper.appendChild(this._els.container);
            this._rowNum = rowNum;
            this._colNum = colNum;
            this._mineNum = mineNum;
            this._generateGrid();
            this._generate();
        }
        // 当格子被点击
        onCellClick(cell) {
            if (!this._isInitial) {
                this._initial(cell);
            }
            this._showTotal++;
            // 点中的格子为雷，则显示所有的格子
            if (cell.isMine) {
                this._grid.forEach(row => row.forEach(cell => cell.show(true)));
                setTimeout(() => {
                    alert('fail');
                }, 20);
                return;
            }
            // 如果点击的格子雷数为0，则需要显示四周的格子
            if (!cell.mineTotal) {
                // 八邻域的格子坐标，并且过滤出符合坐标的格子
                const nextCellPos = [
                    [cell.rowIndex - 1, cell.colIndex - 1],
                    [cell.rowIndex - 1, cell.colIndex],
                    [cell.rowIndex - 1, cell.colIndex + 1],
                    [cell.rowIndex, cell.colIndex - 1],
                    [cell.rowIndex, cell.colIndex + 1],
                    [cell.rowIndex + 1, cell.colIndex - 1],
                    [cell.rowIndex + 1, cell.colIndex],
                    [cell.rowIndex + 1, cell.colIndex + 1],
                ].filter(([ri, ci]) => ri >= 0 && ri < this._rowNum && ci >= 0 && ci < this._colNum);
                nextCellPos.forEach(([ri, ci]) => {
                    const nextCell = this._grid[ri][ci];
                    if (!nextCell.isShow) {
                        nextCell.show();
                        this.onCellClick(nextCell);
                    }
                });
            }
            if (!this._isOver && this._rowNum * this._colNum - this._showTotal - this._mineNum === 0) {
                this._isOver = true;
                this._grid.forEach(row => row.forEach(cell => cell.show()));
                setTimeout(() => {
                    alert('success');
                }, 20);
            }
        }
        onSetFlag(toggle) {
            this._flagTotal += [-1, 1][+toggle];
            this._els.flag.innerText = this._flagTotal;
        }
        // 初始化
        _initial(firstClickcell) {
            this._generateMines(firstClickcell);
            this._calcMineTotal();
            this._isInitial = true;
        }
        _generate() {
            const headerEl = document.createElement('div');
            headerEl.classList.add('minesweeper-header');
            this._els.flag = document.createElement('div');
            this._els.mine = document.createElement('div');
            this._els.flag.classList.add('flag');
            this._els.mine.classList.add('mine');
            this._els.flag.innerText = this._flagTotal;
            this._els.mine.innerText = this._mineNum;
            headerEl.appendChild(this._els.flag);
            headerEl.appendChild(this._els.mine);
            this._els.container.appendChild(headerEl);
            const tableEl = document.createElement('div');
            tableEl.classList.add('minesweeper-table');
            this._els.container.appendChild(tableEl);
            this._grid.forEach((row) => {
                const rowElement = document.createElement('div');
                rowElement.classList.add('minesweeper-row');
                row.forEach((cell) => {
                    rowElement.appendChild(cell.element);
                });
                tableEl.appendChild(rowElement);
            });
        }
        // 生成空白的格子
        _generateGrid() {
            this._grid = new Array(this._rowNum).fill({})
                .map((it, i) => new Array(this._colNum).fill({}).map((it, j) => new Cell(this, i, j)));
        }
        // 根据 行数/列数/雷数 生成雷的位置信息
        _generateMines(firstClickcell) {
            if (this._mineNum >= this._colNum * this._rowNum) {
                throw new Error('雷数不能大于总格子数');
            }
            for (let i = 0; i < this._mineNum;) {
                // 随机生成行列位置
                const r = Math.floor(Math.random() * this._rowNum);
                const c = Math.floor(Math.random() * this._colNum);
                const cell = this._grid[r][c];
                // 如果第一次点击的格子，则不能设置为雷
                if (firstClickcell && firstClickcell.rowIndex === r && firstClickcell.colIndex === c) {
                    continue;
                }
                // 如果已经不是雷，则设置为雷；否则i不增加
                if (!cell.isMine) {
                    cell.setMine();
                    i++;
                }
            }
        }
        // 计算每个格子周围的雷数
        _calcMineTotal() {
            this._grid.forEach((row, rowIndex) => {
                row.forEach((cell, cellIndex) => {
                    // 每个格子需要判断四周 8 个格子是否为雷
                    // [*] [ ] [ ]      p1  p2  p3
                    // [ ]  ?  [*]  ->  p8      p4
                    // [ ] [ ] [ ]      p7  p6  p5
                    // 按照 p1 -> p8 的顺序进行判断
                    // p1, 需要排除边界条件
                    if (rowIndex > 0 && cellIndex > 0 && this._grid[rowIndex - 1][cellIndex - 1].isMine) {
                        cell.setMineTotal(cell.mineTotal + 1);
                    }
                    // p2, 需要排除边界条件
                    if (rowIndex > 0 && this._grid[rowIndex - 1][cellIndex].isMine) {
                        cell.setMineTotal(cell.mineTotal + 1);
                    }
                    // p3, 需要排除边界条件
                    if (rowIndex > 0 && cellIndex < this._colNum - 1 && this._grid[rowIndex - 1][cellIndex + 1].isMine) {
                        cell.setMineTotal(cell.mineTotal + 1);
                    }
                    // p4, 需要排除边界条件
                    if (cellIndex < this._colNum - 1 && this._grid[rowIndex][cellIndex + 1].isMine) {
                        cell.setMineTotal(cell.mineTotal + 1);
                    }
                    // p5, 需要排除边界条件
                    if (rowIndex < this._rowNum - 1 && cellIndex < this._colNum - 1 && this._grid[rowIndex + 1][cellIndex + 1].isMine) {
                        cell.setMineTotal(cell.mineTotal + 1);
                    }
                    // p6, 需要排除边界条件
                    if (rowIndex < this._rowNum - 1 && this._grid[rowIndex + 1][cellIndex].isMine) {
                        cell.setMineTotal(cell.mineTotal + 1);
                    }
                    // p7, 需要排除边界条件
                    if (rowIndex < this._rowNum - 1 && cellIndex > 0 && this._grid[rowIndex + 1][cellIndex - 1].isMine) {
                        cell.setMineTotal(cell.mineTotal + 1);
                    }
                    // p8, 需要排除边界条件
                    if (cellIndex > 0 && this._grid[rowIndex][cellIndex - 1].isMine) {
                        cell.setMineTotal(cell.mineTotal + 1);
                    }
                });
            });
        }
    }

    window.Minesweeper = Minesweeper;
})(window);
